package cn.com.agree.bigdata.vmdatahandle.service;

import cn.com.agree.bigdata.vmdatahandle.config.AppConfig;
import kong.unirest.HttpResponse;
import kong.unirest.Unirest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;

import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;

/**
 * @description:
 * @author: LiYuan
 * @date: 2024/7/9 15:54
 */

@Slf4j
public class WriteWmWorker implements Runnable {
    private static final int ARRAY_INIT_SIZE = 2048;
    private static final int STRING_INIT_SIZE = 2048;
    private static final String COMMA_SYMBOL = ",";
    private static final String CSV_SEPARATOR = ",";

    private AppConfig appConfig;
    private ExecutorService executorService;
    private static final int BATCH = 5000;
    private static int batch = 0;
    private static final String QUERY_TIME_FILE_NAME = "query_time";
    private static final String CSV_SUFFIX = ".csv";
    private static String csvName = "";

    public WriteWmWorker(AppConfig appConfig, ExecutorService executorService) {
        this.appConfig = appConfig;
        this.executorService = executorService;
    }

    @Override
    public void run() {
        log.info("begin read query metrics and query from vm");
        long startTime = System.currentTimeMillis();
        log.info("获取指标查询语句开始时间:{}", startTime);
        List<String> list = getQueryMetrics();
        long endTime = System.currentTimeMillis();
        log.info("获取指标查询语句结束时间:{}，耗时:{}秒", endTime, (endTime - startTime) / 1000.0);
        // 把指标发送到vm
        int threads = appConfig.getVm().getThreads();

        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH_mm_ss_SSS");
        String currentTime = dateFormat.format(new Date());
        if (batch % BATCH == 0) {
            csvName = System.getProperty("user.dir") + File.separator + QUERY_TIME_FILE_NAME + "_" + currentTime + "-" + (batch + 1) + CSV_SUFFIX;
        }
        batch++;
        if (threads > 0) {
            concurrentSendMetricsToWm(list);
        } else {
            sendMetricsToWm(list);
        }
    }

    public String getVmQueryUrl() {
        String server = appConfig.getVm().getServer();
        return String.format("http://%s/api/v1/query", server);
    }

    public void sendMetricsToWm(List<String> list) {
        String url = getVmQueryUrl();
        long startTime = System.currentTimeMillis();
        log.info("查询数据开始时间:{}", startTime);

        // 一次性发送全部指标
        int size = list.size();
        for (int i = 0; i < size; i++) {
            try {
                String param = URLEncoder.encode(list.get(i), "UTF-8");
                String queryUrl = String.format("%s?query=%s", url, param);
                HttpResponse<String> response = Unirest.get(queryUrl).asString();
                String result = response.getBody();
            } catch (Exception e) {
                log.error("query error:", e);
            }
        }
        long sendEndTime = System.currentTimeMillis();
        log.info("发送查询请求结束时间:{}，耗时:{}秒", sendEndTime, (sendEndTime - startTime) / 1000.0);
    }


    public void concurrentSendMetricsToWm(List<String> list) {
        String url = getVmQueryUrl();
        long startTime = System.currentTimeMillis();
        log.info("查询数据开始时间:{}", startTime);

        // 一次性发送全部指标
        // 多线程发送
        ConcurrentLinkedQueue<ExportData> exportDataList = new ConcurrentLinkedQueue<ExportData>();
        int size = list.size();
        final CountDownLatch finishLatch = new CountDownLatch(size);
        for (String queryStr : list) {
            executorService.submit(
                () -> {
                    long threadStartTime = System.currentTimeMillis();
                    try {
                        String param = URLEncoder.encode(queryStr, "UTF-8");
                        String queryUrl = String.format("%s?query=%s", url, param);
                        HttpResponse<String> response = Unirest.get(queryUrl).asString();
                        String result = response.getBody();
                    } catch (Exception e) {
                        log.error("查询指标vm异常:", e);
                    } finally {
                        long threadEndTime = System.currentTimeMillis();
//                        log.info("查询结束时间:{},耗时:{}秒: {}", threadEndTime, (threadEndTime - threadStartTime) / 1000.0, queryStr);
                        ExportData exportData = new ExportData();
                        exportData.setQuery(queryStr);
                        exportData.setCostTime((threadEndTime - threadStartTime) / 1000.0);
                        exportData.setBatch(batch);
                        exportDataList.add(exportData);
                        finishLatch.countDown();
                    }
                }
            );
        }

        // 全部线程发送完毕才进行下一次定时器发送
        try {
            finishLatch.await();
        } catch (InterruptedException e) {
            log.error("CountDownLatch await exception:", e);
        }
        long sendEndTime = System.currentTimeMillis();
        log.info("所有查询结束时间:{}，查询耗时:{}秒", sendEndTime, (sendEndTime - startTime) / 1000.0);

        // 写csv文件
        try {
            StringBuffer sb = new StringBuffer(STRING_INIT_SIZE);
            for(ExportData exportData : exportDataList) {
                sb.append(exportData.getQuery()).append(CSV_SEPARATOR)
                    .append(exportData.getCostTime()).append(CSV_SEPARATOR)
                    .append(exportData.getBatch()).append(System.lineSeparator());
            }
            FileUtils.writeStringToFile(new File(csvName), sb.toString(), StandardCharsets.UTF_8, true);
        } catch (Exception e) {
            log.error("write excel exception:", e);
        }
    }

    public List<String> getQueryMetrics() {
        List<String> list = new ArrayList<>(ARRAY_INIT_SIZE);
        String path = System.getProperty("user.dir");
        String filePath = path + File.separator + "query_metrics.txt";

        int oneMetricsNum = appConfig.getNodeExporter().getOneMetricsNameNum();
        if (oneMetricsNum < 0) {
            oneMetricsNum = 200;
        }
        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = reader.readLine()) != null) {
                if (StringUtils.isBlank(line)) {
                    continue;
                }
                if (StringUtils.startsWith(line, "#")) {
                    continue;
                }
                line = StringUtils.trim(line);
                if (StringUtils.isBlank(line)) {
                    continue;
                }
                for (int i = 0; i < oneMetricsNum; i++) {
                    String newLine = StringUtils.replace(line, "?", String.valueOf(i));
                    list.add(newLine);
                }
            }
        } catch (Exception e) {
            log.error("Exception:", e);
        }
        return list;
    }
}
